import { window, ExtensionContext, QuickInputButton, QuickPickItem, QuickInput, QuickInputButtons, Disposable } from 'vscode';
import { componentCompileLogger } from './logger/componentCompileLogger';
import * as vscode from 'vscode';
import fs from 'fs';
import { getRepoPath } from './utils/commonUtils';
import path from 'path';

export async function componentCompile(context: ExtensionContext, args: any[]) {
	const title = 'Component Compile';

	const state = await collectInputs(title);
    
	if (args.length === 0) {
        vscode.window.showErrorMessage('No args provided, please see the log for more information');
        componentCompileLogger.errorWithStack('No args provided');
        return;
    }

    const uri = args[0] as vscode.Uri;
    if (!uri || typeof uri.fsPath !== 'string') {
        vscode.window.showErrorMessage('Invalid URI provided, , please see the log for more information');
        componentCompileLogger.errorWithStack('Invalid URI provided');
        return;
    }

    let filePath = uri.fsPath;
    
    // Check if path is a file, if not, append bundle.json to it
    if (fs.existsSync(filePath) && fs.statSync(filePath).isDirectory()) {
        filePath = `${filePath}/bundle.json`;
        if (!fs.existsSync(filePath)) {
            vscode.window.showErrorMessage('Could not find bundle.json in the directory, please see the log for more information');
            componentCompileLogger.errorWithStack('Could not find bundle.json in the directory');
            return;
        }
    }

    componentCompileLogger.info(`bundle.json's path is : ${filePath}`);
    const dirPath = filePath.substring(0, filePath.lastIndexOf('/'));
    
    // 从 bundle.json 中读取部件名
    const componentName = getComponentNameFromBundleJson(dirPath);
    if (!componentName) {
        vscode.window.showErrorMessage('Could not get component name from bundle.json, please see the log for more information');
        componentCompileLogger.errorWithStack('Could not get component name from bundle.json');
        return;
    }
    componentCompileLogger.info(`componentName is : ${componentName}`);

    const repoRootPath = getRepoPath(dirPath);
    if (!repoRootPath) {
        componentCompileLogger.error('Could not find .repo directory in any parent folder');
        vscode.window.showErrorMessage(`[${componentName}]: Could not find .repo directory`);
        return;
    }

    vscode.window.showInformationMessage(`[${componentName}]: Compile process has started in the terminal.`);
    executeAndLogCommand(state, dirPath, componentName, repoRootPath);
}

/**
 * 从 bundle.json 中获取部件名
 * 
 * @param dirPath 目录路径
 * @returns 部件名，如果获取失败则返回 null
 */
function getComponentNameFromBundleJson(dirPath: string): string | null {
    try {
        const bundleJsonPath = path.join(dirPath, 'bundle.json');
        if (fs.existsSync(bundleJsonPath)) {
            const bundleContent = fs.readFileSync(bundleJsonPath, 'utf8');
            const bundleJson = JSON.parse(bundleContent);
            
            // 提取部件名
            if (bundleJson.component && bundleJson.component.name) {
                const componentName = bundleJson.component.name;
                componentCompileLogger.info(`Component name from bundle.json: ${componentName}`);
                return componentName;
            } else {
                componentCompileLogger.error('bundle.json does not contain valid component.name field');
                return null;
            }
        } else {
            componentCompileLogger.error(`bundle.json not found at path: ${bundleJsonPath}`);
            return null;
        }
    } catch (error) {
        componentCompileLogger.errorWithStack(`Error reading bundle.json: ${error instanceof Error ? error.message : 'Unknown error'}`);
        return null;
    }
}

/**
 * 执行命令并记录日志
 * 
 * @param state 输入状态
 * @param dirPath 目录路径
 * @param componentName 部件名
 * @param repoRootPath 仓库根目录
 */
function executeAndLogCommand(state: State, dirPath: string, componentName: string, repoRootPath: string) {
    componentCompileLogger.info(`Begin to compile, dirPath is ${dirPath}, componentName is ${componentName}`);

    let commandStr = `hb build ${componentName}`;
    if (state.compileSourceCode) {
        commandStr += ' -i';
    }
    if (state.compileTestCode) {
        commandStr += ' -t';
    }

    componentCompileLogger.info(`command is ${commandStr}`);

    const workingDir = repoRootPath;
    const taskName = `Compile: ${componentName}`;

    const shellExecution = new vscode.ShellExecution(commandStr, {
        cwd: workingDir,
        executable: vscode.env.shell,
        shellArgs: ['-lic']
    });
    const task = new vscode.Task(
        { type: "shell" },
        vscode.TaskScope.Workspace,
        taskName,
        "Component Compile",
        shellExecution
    );

    task.presentationOptions = {
        reveal: vscode.TaskRevealKind.Always,
        panel: vscode.TaskPanelKind.Shared,
        focus: true,
        clear: true
    };

    const endListener = vscode.tasks.onDidEndTaskProcess(event => {
        if (event.execution.task.name === taskName && event.execution.task.source === "Component Compile") {
            if (event.exitCode === 0) {
                const relativePath = readBundleJson(dirPath);
                if (!relativePath) {
                    vscode.window.showErrorMessage(`create build(component-compile) directory failed, please check the log for more information`);
                    return true;
                }

                // 创建build(component-compile)目录
                const buildDirPath = createBuildDirectory(dirPath);
                const path = require('path');
                const outDirPath = createDirectory(path.join(buildDirPath, 'out'));
                const srcDirPath = createDirectory(path.join(outDirPath, 'src'));
                const testDirPath = createDirectory(path.join(outDirPath, 'test'));
                const srcTestDirPath = createDirectory(path.join(outDirPath, 'src_test'));

                // build(component-compile)/out/src/subsystem/
                const buildSrcRelativePathParent = path.join(srcDirPath, path.dirname(relativePath));
                const buildTestRelativePathParent = path.join(testDirPath, path.dirname(relativePath));
                const buildSrcTestRelativePathParent = path.join(srcTestDirPath, path.dirname(relativePath));
                
                // 递归创建父目录
                fs.mkdirSync(buildSrcRelativePathParent, { recursive: true });
                fs.mkdirSync(buildTestRelativePathParent, { recursive: true });
                fs.mkdirSync(buildSrcTestRelativePathParent, { recursive: true });
                
                componentCompileLogger.info(`Created parent directory: ${buildSrcRelativePathParent}`);
                componentCompileLogger.info(`Created parent directory: ${buildTestRelativePathParent}`);
                componentCompileLogger.info(`Created parent directory: ${buildSrcTestRelativePathParent}`);
                
                // 获取目标路径（最后一级目录名称）
                const targetDirName = path.basename(relativePath);
                // build(component-compile)/out/src/subsystem/name
                const buildSrcSubsystemComponentPath = path.join(buildSrcRelativePathParent, targetDirName);
                const buildTestSubsystemComponentPath = path.join(buildTestRelativePathParent, targetDirName);
                const buildSrcTestSubsystemComponentPath = path.join(buildSrcTestRelativePathParent, targetDirName);
                
                // 源目录路径
                const outDefaultPath = path.join(repoRootPath, 'out', 'default');
                // out/default/src/subsystem/name
                const outDefaultSrcSubsystemComponentPath = path.join(outDefaultPath, 'src', relativePath);
                const outDefaultTestSubsystemComponentPath = path.join(outDefaultPath, 'test', relativePath);
                const outDefaultSrcTestSubsystemComponentPath = path.join(outDefaultPath, 'src_test', relativePath);
                
                // 额外的源目录路径 - src
                const outDefaultSrcExeUnstrippedSubsystemComponentPath = path.join(outDefaultPath, 'src', 'exe.unstripped', relativePath);
                const outDefaultSrcLibUnstrippedSubsystemComponentPath = path.join(outDefaultPath, 'src', 'lib.unstripped', relativePath);
                const outDefaultSrcNoticeFilesSubsystemComponentPath = path.join(outDefaultPath, 'src', 'NOTICE_FILES', 'static', 'ohos_clang_arm', relativePath);
                const outDefaultSrcSaProfileSubsystemComponentPath = path.join(outDefaultPath, 'src', 'sa_profile', 'inputs', targetDirName);
                
                // 额外的源目录路径 - test
                const outDefaultTestExeUnstrippedSubsystemComponentPath = path.join(outDefaultPath, 'test', 'exe.unstripped', relativePath);
                const outDefaultTestLibUnstrippedSubsystemComponentPath = path.join(outDefaultPath, 'test', 'lib.unstripped', relativePath);
                const outDefaultTestNoticeFilesSubsystemComponentPath = path.join(outDefaultPath, 'test', 'NOTICE_FILES', 'static', 'ohos_clang_arm', relativePath);
                const outDefaultTestSaProfileSubsystemComponentPath = path.join(outDefaultPath, 'test', 'sa_profile', 'inputs', targetDirName);
                
                // 额外的源目录路径 - src_test
                const outDefaultSrcTestExeUnstrippedSubsystemComponentPath = path.join(outDefaultPath, 'src_test', 'exe.unstripped', relativePath);
                const outDefaultSrcTestLibUnstrippedSubsystemComponentPath = path.join(outDefaultPath, 'src_test', 'lib.unstripped', relativePath);
                const outDefaultSrcTestNoticeFilesSubsystemComponentPath = path.join(outDefaultPath, 'src_test', 'NOTICE_FILES', 'static', 'ohos_clang_arm', relativePath);
                const outDefaultSrcTestSaProfileSubsystemComponentPath = path.join(outDefaultPath, 'src_test', 'sa_profile', 'inputs', targetDirName);

                // src
                createSymlink(outDefaultSrcSubsystemComponentPath, buildSrcSubsystemComponentPath);
                
                // test
                createSymlink(outDefaultTestSubsystemComponentPath, buildTestSubsystemComponentPath);

                // src_test
                createSymlink(outDefaultSrcTestSubsystemComponentPath, buildSrcTestSubsystemComponentPath);
                
                // 为src/exe.unstripped目录创建符号链接
                createSymlink(outDefaultSrcExeUnstrippedSubsystemComponentPath, path.join(srcDirPath, 'exe.unstripped', relativePath));
                
                
                // 为src/lib.unstripped目录创建符号链接
                createSymlink(outDefaultSrcLibUnstrippedSubsystemComponentPath, path.join(srcDirPath, 'lib.unstripped', relativePath));

                
                // 为src/NOTICE_FILES目录创建符号链接
                createSymlink(outDefaultSrcNoticeFilesSubsystemComponentPath, path.join(srcDirPath, 'NOTICE_FILES', 'static', 'ohos_clang_arm', relativePath));
                    
                // 为src/sa_profile目录创建符号链接
                createSymlink(outDefaultSrcSaProfileSubsystemComponentPath, path.join(srcDirPath, 'sa_profile', 'inputs', targetDirName));
                
                // 为test/exe.unstripped目录创建符号链接
                createSymlink(outDefaultTestExeUnstrippedSubsystemComponentPath, path.join(testDirPath, 'exe.unstripped', relativePath));
                
                // 为test/lib.unstripped目录创建符号链接
                createSymlink(outDefaultTestLibUnstrippedSubsystemComponentPath, path.join(testDirPath, 'lib.unstripped', relativePath));
                
                // 为test/NOTICE_FILES目录创建符号链接
                createSymlink(outDefaultTestNoticeFilesSubsystemComponentPath, path.join(testDirPath, 'NOTICE_FILES', 'static', 'ohos_clang_arm', relativePath));
                
                // 为test/sa_profile目录创建符号链接
                createSymlink(outDefaultTestSaProfileSubsystemComponentPath, path.join(testDirPath, 'sa_profile', 'inputs', targetDirName));
                
                // 为src_test/exe.unstripped目录创建符号链接
                createSymlink(outDefaultSrcTestExeUnstrippedSubsystemComponentPath, path.join(srcTestDirPath, 'exe.unstripped', relativePath));
      
                // 为src_test/lib.unstripped目录创建符号链接
                createSymlink(outDefaultSrcTestLibUnstrippedSubsystemComponentPath, path.join(srcTestDirPath, 'lib.unstripped', relativePath));
                
                // 为src_test/NOTICE_FILES目录创建符号链接
                createSymlink(outDefaultSrcTestNoticeFilesSubsystemComponentPath, path.join(srcTestDirPath, 'NOTICE_FILES', 'static', 'ohos_clang_arm', relativePath));
                
                // 为src_test/sa_profile目录创建符号链接
                createSymlink(outDefaultSrcTestSaProfileSubsystemComponentPath, path.join(srcTestDirPath, 'sa_profile', 'inputs', targetDirName));

                vscode.window.showInformationMessage(`[${componentName}]: Compile Success`);
            } else {
                vscode.window.showErrorMessage(`[${componentName}]: Compile failed with exit code ${event.exitCode}.`);
            }
            endListener.dispose();
        }
    });

    vscode.tasks.executeTask(task);
}

function createSymlink(sourcePath: string, targetPath: string) {
    // 复制文件而不是创建符号链接
    if (fs.existsSync(sourcePath)) {
        // 确保目标路径的父目录存在
        createDirectory(path.dirname(targetPath));
        // 如果目标已存在，先删除
        if (fs.existsSync(targetPath)) {
            if (fs.lstatSync(targetPath).isDirectory()) {
                fs.rmSync(targetPath, { recursive: true, force: true });
            } else {
                fs.unlinkSync(targetPath);
            }
        }
        
        // 复制目录
        copyDirectory(sourcePath, targetPath);
        componentCompileLogger.info(`Copied directory from ${sourcePath} to ${targetPath}`);
    } else {
        componentCompileLogger.info(`Source directory not found: ${sourcePath}`);
    }
}


/**
 * 递归复制目录及其内容
 * 
 * @param source 源目录路径
 * @param target 目标目录路径
 */
function copyDirectory(source: string, target: string) {
    // 创建目标目录
    fs.mkdirSync(target, { recursive: true });
    
    // 读取源目录中的所有文件和子目录
    const items = fs.readdirSync(source);
    
    for (const item of items) {
        const sourcePath = path.join(source, item);
        const targetPath = path.join(target, item);
        
        // 检查是文件还是目录
        const stat = fs.statSync(sourcePath);
        
        if (stat.isDirectory()) {
            // 递归复制子目录
            copyDirectory(sourcePath, targetPath);
        } else {
            // 复制文件
            fs.copyFileSync(sourcePath, targetPath);
        }
    }
}

/**
 * 创建目录
 * 
 * @param buildDirPath 构建目录路径
 * @returns 输出目录路径
 */
function createDirectory(dirPath: string): string {
    if (!fs.existsSync(dirPath)) {
        fs.mkdirSync(dirPath, { recursive: true });
        componentCompileLogger.info(`Created directory at: ${dirPath}`);
    }
    return dirPath;
}

/**
 * 创建或清空build目录
 * 
 * @param dirPath 目录路径
 * @returns 构建目录路径
 */
function createBuildDirectory(dirPath: string): string {
    // 创建或清空build(component-compile)目录
    const buildDirPath = path.join(dirPath, 'build(component-compile)');
    
    // 检查build(component-compile)目录是否存在
    if (fs.existsSync(buildDirPath)) {
        // 如果存在，清空目录内容
        fs.readdirSync(buildDirPath).forEach((file: string) => {
            const curPath = path.join(buildDirPath, file);
            if (fs.lstatSync(curPath).isDirectory()) {
                // 递归删除子目录
                fs.rmSync(curPath, { recursive: true, force: true });
            } else {
                // 删除文件
                fs.unlinkSync(curPath);
            }
        });
        componentCompileLogger.info(`Cleaned existing build(component-compile) directory at: ${buildDirPath}`);
    } else {
        // 如果不存在，创建目录
        fs.mkdirSync(buildDirPath);
        componentCompileLogger.info(`Created build(component-compile) directory at: ${buildDirPath}`);
    }
    return buildDirPath;
}

/**
 * 读取bundle.json文件，并返回componentPath，如果读取失败，则返回null
 * 
 * @param dirPath 目录路径
 * @returns 组件路径，如果读取失败，则返回null
 */
function readBundleJson(dirPath: string): string | null {
    // 读取bundle.json文件
    try {
        const bundleJsonPath = path.join(dirPath, 'bundle.json');
        if (fs.existsSync(bundleJsonPath)) {
            const bundleContent = fs.readFileSync(bundleJsonPath, 'utf8');
            const bundleJson = JSON.parse(bundleContent);
            
            // 提取subsystem和name
            if (bundleJson.component && bundleJson.component.subsystem && bundleJson.component.name) {
                const subsystem = bundleJson.component.subsystem;
                const name = bundleJson.component.name;
                const componentPath = `${subsystem}/${name}`;
                
                componentCompileLogger.info(`Component path from bundle.json: ${componentPath}`);
                return componentPath;
            }
        }
        return null;
    } catch (error) {
        componentCompileLogger.errorWithStack(`Error reading bundle.json: ${error instanceof Error ? error.message : 'Unknown error'}`);
        return null;
    }
}

interface State {
    title: string;
    step: number;
    totalSteps: number;
    compileSourceCode: boolean;
    compileTestCode: boolean;
    buildFormFactor: "RK3568",
}

async function collectInputs(title: string) {
    const state = {} as Partial<State>;
    const compileTypeItems: QuickPickItem[] = ['源码', '测试用例'].map(label => ({ label }));
    await MultiStepInput.run(input => pickCompileType(compileTypeItems, title, input, state));
    return state as State;
}

async function pickCompileType(
    items: QuickPickItem[],
    title: string, input: MultiStepInput, state: Partial<State>): Promise<InputStep | void> {
    const pick = await input.showQuickPick(true, {
        title,
        step: 1,
        totalSteps: 2,
        placeholder: 'Pick a compile type, then press Enter to continue',
        items: items,
        activeItem: undefined,
        buttons: [],
        shouldResume: () => Promise.resolve(false)
    });

    const selectedItems = Array.isArray(pick) ? pick.map(item => item.label) : [];
    if (selectedItems.length === 0) {
        componentCompileLogger.errorWithStack('No compile type selected');
        state.compileSourceCode = undefined;
        state.compileTestCode = undefined;
    } else {
        // Update state based on selections
        state.compileSourceCode = selectedItems.includes('源码');
        state.compileTestCode = selectedItems.includes('测试用例');
    }

    // Move to the next step only when user confirms selection
    const factorItems: QuickPickItem[] = ['RK3568'].map(label => ({ label }));
    return (input: MultiStepInput) => pickBuildFormFactor(factorItems, 'Pick a build form factor', input, state);
}


async function pickBuildFormFactor(
    items: QuickPickItem[],
    title: string, input: MultiStepInput, state: Partial<State>): Promise<InputStep | void> {

    // 仅展示
    try {
        const pick = await input.showQuickPick(false, {
            title,
            step: 2,
            totalSteps: 2,
            placeholder: 'Pick a build form factor',
            items: items,
            activeItem: items[0], // 默认选择第一个项目
            buttons: [],
            shouldResume: () => Promise.resolve(false)
        });
        
        // 设置构建形态状态（虽然目前只有一个选项）
        if (Array.isArray(pick) && pick.length > 0) {
            state.buildFormFactor = pick[0].label as "RK3568";
        } else {
            state.buildFormFactor = "RK3568"; // 默认值
        }
    } catch (err) {
        if (err === InputFlowAction.back) {
            // 返回上一步
            const compileTypeItems: QuickPickItem[] = ['源码', '测试用例'].map(label => ({ label }));
            return (input: MultiStepInput) => pickCompileType(compileTypeItems, 'Component Compile', input, state);
        }
        throw err;
    }
}


type InputStep = (input: MultiStepInput) => Thenable<InputStep | void>;

class InputFlowAction {
	static back = new InputFlowAction();
	static cancel = new InputFlowAction();
	static resume = new InputFlowAction();
}

interface QuickPickParameters<T extends QuickPickItem> {
	title: string;
	step: number;
	totalSteps: number;
	items: T[];
	activeItem?: T;
	ignoreFocusOut?: boolean;
	placeholder: string;
	buttons?: QuickInputButton[];
	shouldResume: () => Thenable<boolean>;
}

class MultiStepInput {

	static async run(start: InputStep) {
		const input = new MultiStepInput();
		return input.stepThrough(start);
	}

	private current?: QuickInput;
	private steps: InputStep[] = [];

	private async stepThrough(start: InputStep) {
		let step: InputStep | void = start;
		while (step) {
			this.steps.push(step);
			// dispose是会有延迟的
			if (this.current) {
				this.current.enabled = false;
				this.current.busy = true;
			}
			try {
				step = await step(this);
			} catch (err) {
				if (err === InputFlowAction.back) {
					this.steps.pop();
					step = this.steps.pop();
				} else if (err === InputFlowAction.resume) {
					step = this.steps.pop();
				} else if (err === InputFlowAction.cancel) {
					step = undefined;
				} else {
					throw err;
				}
			}
		}
		if (this.current) {
			this.current.dispose();
		}
	}

	async showQuickPick<T extends QuickPickItem, P extends QuickPickParameters<T>>(canSelectMany: boolean, {title, step, totalSteps, items, activeItem, ignoreFocusOut, placeholder, buttons, shouldResume }: P) {
		const disposables: Disposable[] = [];
		try {
			return await new Promise<T[] | (P extends { buttons: (infer I)[] } ? I : never)>((resolve, reject) => {
				const input = window.createQuickPick<T>();
				input.canSelectMany = canSelectMany;
				input.title = title;
				input.step = step;
				input.totalSteps = totalSteps;
				input.ignoreFocusOut = ignoreFocusOut ?? false;
				input.placeholder = placeholder;
				input.items = items;
			
				if (activeItem) {
					input.activeItems = [activeItem];
				}
				input.buttons = [
					...(this.steps.length > 1 ? [QuickInputButtons.Back] : []),
					...(buttons || [])
				];

				disposables.push(
					input.onDidAccept(() => {
						if (input.selectedItems.length > 0) {
							resolve(Array.from(input.selectedItems));
						} else {
							// 如果没有选择项，提示用户
							window.showInformationMessage('Please select at least one item');
						}
					}),
					input.onDidTriggerButton(item => {
						if (item === QuickInputButtons.Back) {
							reject(InputFlowAction.back);
						} else {
							resolve(<any>item);
						}
					})
				);

				if (this.current) {
					this.current.dispose();
				}
				this.current = input;
				this.current.show();
			});
		} finally {
			disposables.forEach(d => d.dispose());
		}
	}
}